/*
 * @author: wizardc
 */

using System;
using System.Collections.Generic;
using Dou.Utils;
using UnityEngine;

namespace Dou.ScreenController
{
    /// <summary>
    /// UI 屏幕适配管理器
    /// </summary>
    public sealed class ScreenUIAdapterMgr
    {
        private static ScreenUIAdapterMgr _instance;
        public static ScreenUIAdapterMgr instance => _instance ??= new ScreenUIAdapterMgr();
        
        /// <summary>
        /// 屏幕模式改变时
        /// 注：手动改变时也会抛出该事件
        /// </summary>
        public event Action<UIScreenMode> onScreenModeChanged;
        
        /// <summary>
        /// 微调模式改变时
        /// 注：手动改变时也会抛出该事件
        /// </summary>
        public event Action<UIFineTuningMode> onFineTuningModeChanged;
        
        private Vector2 _landscapeDesignResolution = Vector2.zero;
        private Vector2 _portraitDesignResolution = Vector2.zero;
        private float _landscapeMatchWidthOrHeight;
        
        private Vector4 _safeAreaOffset = Vector4.zero;
        private Vector4 _safeAreaOffsetOnlyOneSide = Vector4.zero;
        
        private List<CanvasScalerAdapter> _canvasScalerList = new List<CanvasScalerAdapter>();
        private List<SafeAreaAdapter> _safeAreaList = new List<SafeAreaAdapter>();
        private List<SafeAreaRestoreAdapter> _safeAreaRestoreList = new List<SafeAreaRestoreAdapter>();
        
        private UIScreenMode _uiScreenMode = UIScreenMode.Landscape;
        private UIFineTuningMode _uiFineTuningMode = UIFineTuningMode.None;
        
        private List<RectTransformAdapter> _rectTransformList = new List<RectTransformAdapter>();
        private List<ActiveAdapter> _activeList = new List<ActiveAdapter>();
        private List<HorizontalOrVerticalLayoutGroupAdapter> _horizontalOrVerticalLayoutList = new List<HorizontalOrVerticalLayoutGroupAdapter>();
        private List<DTextMeshProUGUIAdapter> _textMeshProUGUIList = new List<DTextMeshProUGUIAdapter>();
        
        private ScreenUIAdapterMgr()
        {
        }
        
        /// <summary>
        /// 水平（横屏）设计分辨率
        /// </summary>
        public Vector2 landscapeDesignResolution => _landscapeDesignResolution;
        
        /// <summary>
        /// 垂直（竖屏）设计分辨率
        /// </summary>
        public Vector2 portraitDesignResolution => _portraitDesignResolution;

        /// <summary>
        /// 横屏时的 matchWidthOrHeight 值，竖屏时会用 1 - 该值
        /// </summary>
        public float landscapeMatchWidthOrHeight => _landscapeMatchWidthOrHeight;
        
        /// <summary>
        /// 刘海屏 UI 使用的安全区域偏移
        /// </summary>
        public Vector4 safeAreaOffset => _safeAreaOffset;

        /// <summary>
        /// 刘海屏 UI 使用的安全区域偏移（仅一侧），横屏时仅保留左右偏移，竖屏时仅保留上下偏移
        /// </summary>
        public Vector4 safeAreaOffsetOnlyOneSide => _safeAreaOffsetOnlyOneSide;

        /// <summary>
        /// UI 当前使用的主布局
        /// </summary>
        public UIScreenMode uiScreenMode
        {
            set
            {
                if (_uiScreenMode != value)
                {
                    _uiScreenMode = value;
                    RefreshAll();
                    onScreenModeChanged?.Invoke(value);
                }
            }
            get => _uiScreenMode;
        }

        /// <summary>
        /// UI 当前使用的微调布局
        /// </summary>
        public UIFineTuningMode uiFineTuningMode
        {
            set
            {
                if (_uiFineTuningMode != value)
                {
                    _uiFineTuningMode = value;
                    RefreshAll();
                    onFineTuningModeChanged?.Invoke(value);
                }
            }
            get => _uiFineTuningMode;
        }
        
        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="uiDesignWidth">横屏时的 UI 设计宽度</param>
        /// <param name="uiDesignHeight">横屏时的 UI 设计高度</param>
        /// <param name="matchWidthOrHeight">横屏时的 matchWidthOrHeight 值</param>
        public void Init(int designWidth, int designHeight, int matchWidthOrHeight)
        {
            _landscapeDesignResolution.Set(designWidth, designHeight);
            _portraitDesignResolution.Set(designHeight, designWidth);
            _landscapeMatchWidthOrHeight = matchWidthOrHeight;
            
            ScreenManager.instance.onScreenIsLandscapeChanged += OnScreenIsLandscapeChanged;
            OnScreenIsLandscapeChanged(ScreenManager.instance.isLandscape);
        }

        /// <summary>
        /// 添加一个适配组件
        /// </summary>
        public void AddAdapter(IUIAdapter adapter)
        {
            if (adapter is CanvasScalerAdapter)
            {
                _canvasScalerList.Add((CanvasScalerAdapter) adapter);
            }
            else if (adapter is SafeAreaAdapter)
            {
                _safeAreaList.Add((SafeAreaAdapter) adapter);
            }
            else if (adapter is SafeAreaRestoreAdapter)
            {
                _safeAreaRestoreList.Add((SafeAreaRestoreAdapter) adapter);
            }
            else if (adapter is RectTransformAdapter)
            {
                _rectTransformList.Add((RectTransformAdapter) adapter);
            }
            else if (adapter is ActiveAdapter)
            {
                _activeList.Add((ActiveAdapter) adapter);
            }
            else if (adapter is HorizontalOrVerticalLayoutGroupAdapter)
            {
                _horizontalOrVerticalLayoutList.Add((HorizontalOrVerticalLayoutGroupAdapter) adapter);
            }
            else if (adapter is DTextMeshProUGUIAdapter)
            {
                _textMeshProUGUIList.Add((DTextMeshProUGUIAdapter) adapter);
            }
            else
            {
                Log.Warning($"UI适配类型\"{adapter.GetType().FullName}\"未支持！");
            }
            
            adapter.RefreshAdapter((int) _uiScreenMode, (int) _uiFineTuningMode);
        }

        /// <summary>
        /// 移除一个适配组件
        /// </summary>
        public void RemoveAdapter(IUIAdapter adapter)
        {
            if (adapter is CanvasScalerAdapter)
            {
                _canvasScalerList.Remove((CanvasScalerAdapter) adapter);
            }
            else if (adapter is SafeAreaAdapter)
            {
                _safeAreaList.Remove((SafeAreaAdapter) adapter);
            }
            else if (adapter is SafeAreaRestoreAdapter)
            {
                _safeAreaRestoreList.Remove((SafeAreaRestoreAdapter) adapter);
            }
            else if (adapter is RectTransformAdapter)
            {
                _rectTransformList.Remove((RectTransformAdapter) adapter);
            }
            else if (adapter is ActiveAdapter)
            {
                _activeList.Remove((ActiveAdapter) adapter);
            }
            else if (adapter is HorizontalOrVerticalLayoutGroupAdapter)
            {
                _horizontalOrVerticalLayoutList.Remove((HorizontalOrVerticalLayoutGroupAdapter) adapter);
            }
            else if (adapter is DTextMeshProUGUIAdapter)
            {
                _textMeshProUGUIList.Remove((DTextMeshProUGUIAdapter) adapter);
            }
            else
            {
                Log.Warning($"UI适配类型\"{adapter.GetType().FullName}\"未支持！");
            }
        }

        private void OnScreenIsLandscapeChanged(bool isLandscape)
        {
            var xRatio = (isLandscape ? _landscapeDesignResolution.x : _portraitDesignResolution.x) / Screen.width;
            var yRatio = (isLandscape ? _landscapeDesignResolution.y : _portraitDesignResolution.y) / Screen.height;
            
            _safeAreaOffset.x = Screen.safeArea.xMin * xRatio;
            _safeAreaOffset.y = Screen.safeArea.yMin * yRatio;
            _safeAreaOffset.z = (Screen.width - Screen.safeArea.xMax) * xRatio;
            _safeAreaOffset.w = (Screen.height - Screen.safeArea.yMax) * yRatio;

            _safeAreaOffsetOnlyOneSide = _safeAreaOffset;
            if (isLandscape)
            {
                _safeAreaOffsetOnlyOneSide.y = 0;
                _safeAreaOffsetOnlyOneSide.w = 0;
            }
            else
            {
                _safeAreaOffsetOnlyOneSide.x = 0;
                _safeAreaOffsetOnlyOneSide.z = 0;
            }
            
            _uiScreenMode = isLandscape ? UIScreenMode.Landscape : UIScreenMode.Portrait;

            RefreshAll();
            
            onScreenModeChanged?.Invoke(_uiScreenMode);
        }
        
#if UNITY_EDITOR

        public void SimulateSafeArea(float leftOrTopPercent, float rightOrBottomPercent)
        {
            var isLandscape = ScreenManager.instance.isLandscape;
            var xRatio = (isLandscape ? _landscapeDesignResolution.x : _portraitDesignResolution.x) / Screen.width;
            var yRatio = (isLandscape ? _landscapeDesignResolution.y : _portraitDesignResolution.y) / Screen.height;

            _safeAreaOffset.x = Screen.width * leftOrTopPercent * xRatio;
            _safeAreaOffset.y = Screen.height * rightOrBottomPercent * yRatio;
            _safeAreaOffset.z = Screen.width * rightOrBottomPercent * xRatio;
            _safeAreaOffset.w = Screen.height * leftOrTopPercent * yRatio;

            _safeAreaOffsetOnlyOneSide = _safeAreaOffset;
            if (isLandscape)
            {
                _safeAreaOffsetOnlyOneSide.y = 0;
                _safeAreaOffsetOnlyOneSide.w = 0;
            }
            else
            {
                _safeAreaOffsetOnlyOneSide.x = 0;
                _safeAreaOffsetOnlyOneSide.z = 0;
            }
            
            RefreshAll();
            
            onScreenModeChanged?.Invoke(_uiScreenMode);
        }

#endif

        private void RefreshAll()
        {
            var uiScreenMode = (int) _uiScreenMode;
            var uiFineTuningMode = (int) _uiFineTuningMode;
            
            RefreshCanvasScaler(uiScreenMode, uiFineTuningMode);
            RefreshSafeArea(uiScreenMode, uiFineTuningMode);
            RefreshUIContent(uiScreenMode, uiFineTuningMode);
        }

        private void RefreshCanvasScaler(int uiScreenMode, int uiFineTuningMode)
        {
            foreach (var adapter in _canvasScalerList)
            {
                adapter.RefreshAdapter(uiScreenMode, uiFineTuningMode);
            }
        }

        private void RefreshSafeArea(int uiScreenMode, int uiFineTuningMode)
        {
            foreach (var adapter in _safeAreaList)
            {
                adapter.RefreshAdapter(uiScreenMode, uiFineTuningMode);
            }
            foreach (var adapter in _safeAreaRestoreList)
            {
                adapter.RefreshAdapter(uiScreenMode, uiFineTuningMode);
            }
        }

        private void RefreshUIContent(int uiScreenMode, int uiFineTuningMode)
        {
            foreach (var adapter in _rectTransformList)
            {
                adapter.RefreshAdapter(uiScreenMode, uiFineTuningMode);
            }
            foreach (var adapter in _activeList)
            {
                adapter.RefreshAdapter(uiScreenMode, uiFineTuningMode);
            }
            foreach (var adapter in _horizontalOrVerticalLayoutList)
            {
                adapter.RefreshAdapter(uiScreenMode, uiFineTuningMode);
            }
            foreach (var adapter in _textMeshProUGUIList)
            {
                adapter.RefreshAdapter(uiScreenMode, uiFineTuningMode);
            }
        }
    }
}
